package org.hilo.boot.core.web;

import java.io.IOException;
import java.io.InputStream;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;

import org.jretty.util.ByteArrayOutputStream;

/**
 * 专为多次读取 HttpServletRequest.getInputStream() 而设计的缓存方案。
 * 第二次读取时，调用一下 reset() 方法即可。
 * 
 * @author zollty
 * @since 2020年11月18日
 */
public class CachedServletInputStream extends ServletInputStream {
    private InputStream sourceStream;
    private ByteArrayOutputStream cacheStream;

    public CachedServletInputStream(InputStream inputStream) throws IOException {
        if (inputStream.markSupported()) {
            inputStream.mark(1);
            setSourceStream((inputStream.read() != -1 ? inputStream : null));
            inputStream.reset();
        } else {
            cacheStream = new ByteArrayOutputStream();
            cacheStream.write(inputStream);
            setSourceStream(cacheStream.toInputStream());
        }
    }
    
    public CachedServletInputStream(ByteArrayOutputStream stream) {
        this.cacheStream = stream;
        setSourceStream(cacheStream.toInputStream());
    }

    @Override
    public void reset() throws IOException {
        // if (getSourceStream() != null) {
        // getSourceStream().reset();
        // } else if (cacheStream != null) { // 重新赋值
        // cacheStream.reset();
        // setSourceStream(cacheStream.toInputStream());
        // }
        if (cacheStream != null) { // 重新赋值
            // try {
            // cacheStream.reset();
            // } catch (Exception e) {
            // // ignore..
            // }
            try {
                setSourceStream(cacheStream.toInputStream());
            } catch (Exception e) {
                // ignore..
            }
        } else {
            if (getSourceStream() != null) {
                try {
                    getSourceStream().reset();
                } catch (Exception e) {
                    // ignore..
                }
            }
        }
    }
    
    @Override
    public int read() throws IOException {
        return this.sourceStream.read();
    }

    @Override
    public boolean isFinished() {
        return false;
    }

    @Override
    public boolean isReady() {
        return true;
    }

    @Override
    public void setReadListener(ReadListener listener) {
        throw new UnsupportedOperationException();
    }
    
    /**
     * Return the underlying source stream (never {@code null}).
     */
    public final InputStream getSourceStream() {
        return this.sourceStream;
    }

    public void setSourceStream(InputStream sourceStream) {
        this.sourceStream = sourceStream;
    }

}
